#pragma once

#include "MatchStructs.h"
#include "Pattern.h"

using namespace System::Text;

namespace SpikingNeuronLib
{
	// see ReportMatch()
	public delegate void AggregatedMatchEventHandler(String^, int, int, int, int, String^, array<FiringEvent>^);

	public ref class MatchTemplate
	{
	private:
		int _jitter;					// +/- this value
		int _jitterRange;				// the window size = jitter * 2 + 1 (also the time range over which aggregation occurs)
		int _offsetMin;					// earliest firing time in the firing event list
		int _offsetMax;					// earliest firing time in the firing event list
		int _timespan;					// the range of time covered by this template (including jitter)
		int _eventMatches;				// the number of firing event matches in the current match against the data
		int _matchThreshold;			// the minimum number of matching firing events that determine a template match
		
		Dictionary<FiringEvent, array<int>^>^ _ranges;
		List<FiringEvent>^ _firingEvents;
		array<bool>^ _matchedEvents;		// the firing events within the template that matched

		// Match aggregation data structures
		int _previousTimeIndex;						// time index of previous match
		int _aggregatorIndex;						// current index into the array of matches
		array<MatchIndex>^ _matchAggregator;		// aggregation buffer
		AggregatedMatchEventHandler^ _matchHandler;	// report an aggregated match

		// Match aggregation methods
		void ReportMatch(String^ tag);

	public:
		property int TemplateID;		// index in TemplateList

		property int UniqueID;			// unique ID for this Pattern (from Pattern UniqueID)

		property int WindowSize
		{
		public:
			int get() { return _jitterRange; }
		}

		property int Timespan
		{
		public:
			int get() { return _timespan; }
		}

		property Dictionary<FiringEvent, array<int>^>^ TemplateRanges
		{
		public:
			Dictionary<FiringEvent, array<int>^>^ get() { return _ranges; }
		}

		property Pattern^ Template
		{
		public:
			Pattern^ get() { return gcnew Pattern(_firingEvents); }
		}

		MatchTemplate(int templateID, Pattern^ pattern, int jitter, int matchThreshold);

		// match this template against the spike raster at the specified raster index
		// timeIndex is the corresponding temporal index (ignoring segment offset)
		// matches are aggregated
		void Match(String^ tag, array<int, 2>^ spikeRaster, int rasterIndex, int timeIndex);
		void AggregationFlush(String^ tag);

		virtual String^ ToString() override;

		array<array<int>^>^ GetFiringsArray();			// for better compatibility w/ Python etc.

		array<int>^ GetRange(FiringEvent firingEvent);
		MatchRaster GetMatch(array<int, 2>^ dataRaster, int offset);

		// Match aggregation methods
		void AddMatchHandler(AggregatedMatchEventHandler^ matchHandler);
	};
}
